﻿// 服务器.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <stdio.h>
#include <string.h>

#include <winsock.h>
#pragma comment(lib,"Ws2_32.lib")


/*
聊天室：
    服务器可以接受多个客户端的链接     简易区分

    在接受链接的同时
        某个客户端发数据过来      接收
        先输出到控制台     然后发送给每个当前链接到服务器的客户端


 客户端：
    先链接上服务器
    同时做两件事：
        1.循环接收用户的输入，并发送到服务器
        2.循环接收服务器发来的数据并显示


        用多线程实现

*/


//全局变量：
    //为了同时接受多个客户端   必须设成数组接受
SOCKET clientSocket[1024];
    //统计当前连接服务器的数量
int g_cnt;

//线程函数
void threadFunc(int idx);


int main()
{
    g_cnt = 0;


    //0.确定协议版本号 
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        printf("确定协议版本号失败%d\n", GetLastError());
        Sleep(5000);
        return -1;
    }
    printf("确定协议版本号成功\n");


    //1.创建socket
    SOCKET serverSocket = socket(AF_INET /* internetwork: UDP, TCP, etc. */, SOCK_STREAM/*字节流 二进制*/, IPPROTO_TCP/* tcp */);
    if (SOCKET_ERROR == serverSocket)
    {
        printf("创建socket失败%d\n", GetLastError());

        //8.清理协议版本信息
        WSACleanup();

        Sleep(5000);
        return -1;
    }
    printf("创建socket成功\n");


    //2..创建服务器协议地址族
    SOCKADDR_IN sadder = { 0 };
    sadder.sin_family = AF_INET/* internetwork: UDP, TCP, etc. */;      //与第一个参数一致
    sadder.sin_addr.S_un.S_addr = inet_addr("10.133.98.104");      //ip地址      作用是区分主机
    sadder.sin_port = htons(10086);  //大小端模式   一般pc机都是小端      网络服务器是unix系统，是大端       所以把整数从小端转成大端
    //我们电脑一般的端口号是65536个     并且一个应用程序绑定一个端口号    一个程序绑定了 另外一个程序就不能用了      所以使用5000以上的   10000左右的最好     数太大没什么用 数太小有可能别的程序用了   


    //3.绑定
    int r = bind(serverSocket, (struct sockaddr*)&sadder, sizeof(sadder));
    if (r == -1)
    {
        printf("绑定失败%d\n", GetLastError());

        //7.关闭socket
        closesocket(serverSocket);

        //8.清理协议版本信息
        WSACleanup();

        Sleep(5000);
        return -1;
    }
    printf("绑定成功\n");


    //4.监听
    r = listen(serverSocket, 10);
    if (r == -1)
    {
        printf("监听失败%d\n", GetLastError());

        //7.关闭socket
        closesocket(serverSocket);

        //8.清理协议版本信息
        WSACleanup();

        Sleep(5000);
        return -1;
    }
    printf("监听成功\n");


    //5.接受客户端连接
   
        //客户端协议地址族：
    SOCKADDR_IN cadder = { 0 };
    int len = sizeof(cadder);



   

    for (int i = 0; i < 1024; i++)
    {
        clientSocket[i] = accept(serverSocket,(struct sockaddr*)&cadder/*把客户端的信息给接收到        把参数当返回值用*/, &len);
        if (SOCKET_ERROR == clientSocket[i])
        {
            printf("服务器崩溃了%d\n", GetLastError());

            //7.关闭socket
            closesocket(serverSocket);

            //8.清理协议版本信息
            WSACleanup();

            Sleep(5000);
            return -1;
        }
        printf("有客户端连接上服务器了 %s\n",inet_ntoa(cadder.sin_addr)/*ip地址*/);

        g_cnt++;    //统计连接服务器数量


        //创建线程;
        CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)threadFunc, (LPVOID)i, NULL, NULL);

    }
        
    


    


    //7.关闭socket
    closesocket(serverSocket);


    //8，清理协议版本信息
    WSACleanup();

    //注意单有server    不要直接运行

    return 0;
}

void threadFunc(int idx)
{
    //6.通信      客户端发数据给服务器 服务器输出到控制台
    char buff[1024];
    char temp[1024];
    int r;
    while (1)
    {
        r = recv(clientSocket[idx]/*从哪里接收*/, buff/*接收到哪*/, 1023/*接收多大*/, NULL);  //和读文件一致
        if (r > 0)
        {
            buff[r] = 0; //添加结束符号   '\0'        r作为坐标刚好是末尾的下一个 所以有结束符号作用
           
            memset(temp, 0, 1024);     //清空
            sprintf(temp, "The sequence number of the server:%d:%s\n", idx + 1, buff);
            printf(">>%s\n", temp);
            //发送给连上当前服务器的每个客户端
            for (int i = 0; i < g_cnt; i++)
            {
                send(clientSocket[i], temp, strlen(temp), NULL);

            }

        }
        else
        {
            break;
        }
    }
}
